package com.c6906.vdam.dps.iotdb;

import com.c6906.vdam.dps.common.DateUtils;
import com.c6906.vdam.dps.entity.TerminalData;
import org.apache.commons.lang.StringUtils;
import org.apache.iotdb.isession.pool.SessionDataSetWrapper;
import org.apache.iotdb.rpc.IoTDBConnectionException;
import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.iotdb.session.pool.SessionPool;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.read.common.Field;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author : TAOHUIQI
 * @Date: 2024/6/16 14:01
 * @Description:iotDB操作类
 */
public class IotDBOperate {

    private static final Logger logger = LoggerFactory.getLogger(IotDBOperate.class);

    //正式库名
    private static final String IOTDB_DATABASE_NAME = "root.hn.vdam";
    //压测库名
//    private static final String IOTDB_DATABASE_NAME = "root.hn.vdam_uat";

    //正式库路径
    private static final String IOTDB_DATABASE_PATH = "root.hn.vdam.";
    //压测库路劲
//    private static final String IOTDB_DATABASE_PATH = "root.hn.vdam_uat.";

    private static final String IOTDB_TEMPLATE_NAME = "vdam_template";

    private static final String PRE_IOTDB_SN = "SN";

    /*
     * 校验StorageGroup单个节点是否存在
     */
    public static boolean checkStorageGroupExists(String deviceId) {
        List<String> devicePaths = getAllStorageGroupNos();
        String sn = IOTDB_DATABASE_PATH + PRE_IOTDB_SN + deviceId;
        if (devicePaths.contains(sn)) {
            logger.info("该设备在IotDB已经初始化:==={}", IOTDB_DATABASE_PATH + PRE_IOTDB_SN + deviceId);
            return true;
        }
        return false;
    }

    /*
     * 查询IOtDB所有DeviceList
     */
    public static List<String> getAllStorageGroupNos() {
        SessionPool sessionPool = IotDBConfig.getConnection();
        SessionDataSetWrapper wrapper = null;
        List<String> devicePaths = new ArrayList<>();
        try {
            wrapper = sessionPool.executeQueryStatement("SHOW DEVICES");
            while (wrapper.hasNext()) {
                List<Field> list = wrapper.next().getFields();
                //获取已经存在的device路径
                devicePaths.add(list.get(0).getBinaryV().toString());
            }
            //去重
            return devicePaths.stream().distinct().collect(Collectors.toList());
        } catch (IoTDBConnectionException e) {
            throw new RuntimeException(e);
        } catch (StatementExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    /*
     * 根据设备模板创建Timeseries节点
     */
    public static void createAlignedTimeseries(List<String> deviceList) throws IoTDBConnectionException, StatementExecutionException {
        SessionPool sessionPool = IotDBConfig.getConnection();
        List<String> devicePaths = getAllStorageGroupNos();
        try {
            for (String terminalSn : deviceList) {
                String deviceId = IOTDB_DATABASE_PATH + PRE_IOTDB_SN + terminalSn;
                //判断设备是否存在
                if (!devicePaths.contains(deviceId)) {
                    String sql = "create timeseries using DEVICE TEMPLATE on DevicePath;";
                    String endSql = sql.replaceAll("DevicePath", deviceId);
                    sessionPool.executeNonQueryStatement(endSql);
                    logger.info("根据该设备编码创建时序节点成功,编码为====>>{}", deviceId);
                } else {
                    logger.info("当前设备编码已经创建过时序节点，无法继续创建,编码为====>>{}", deviceId);
                }
            }
        } catch (Exception e) {
            logger.error("createAlignedTimeseries error", e);
        }
    }

    /*
     * 创建设备模板
     */
    public static String createDeviceTemplate() {
        try {
            SessionPool sessionPool = IotDBConfig.getConnection();
            //创建设备模板
            String templateSql = "create device template CONFIG_TEMPLATE_NAME aligned (terminal_sn TEXT encoding=PLAIN compression=SNAPPY, terminal_sim TEXT encoding=PLAIN compression=SNAPPY, is_fixed TEXT encoding=PLAIN compression=SNAPPY, longitude FLOAT encoding=GORILLA compression=SNAPPY, latitude FLOAT encoding=GORILLA compression=SNAPPY, elevation TEXT encoding=PLAIN compression=SNAPPY, direction TEXT encoding=PLAIN compression=SNAPPY, terminal_time TEXT encoding=PLAIN compression=SNAPPY, terminal_miles TEXT encoding=PLAIN compression=SNAPPY, terminal_miles_aday TEXT encoding=PLAIN compression=SNAPPY, speed TEXT encoding=PLAIN compression=SNAPPY, terminal_off_line TEXT encoding=PLAIN compression=SNAPPY, terminal_over_speed TEXT encoding=PLAIN compression=SNAPPY, terminal_tired_drive TEXT encoding=PLAIN compression=SNAPPY, terminal_gnss_error TEXT encoding=PLAIN compression=SNAPPY, terminal_ant_open TEXT encoding=PLAIN compression=SNAPPY, terminal_ant_short TEXT encoding=PLAIN compression=SNAPPY, terminal_under_vol TEXT encoding=PLAIN compression=SNAPPY, terminal_no_vol TEXT encoding=PLAIN compression=SNAPPY, terminal_acc_status TEXT encoding=PLAIN compression=SNAPPY, terminal_net_rssi TEXT encoding=PLAIN compression=SNAPPY, terminal_gnss_cnt TEXT encoding=PLAIN compression=SNAPPY, fix_beidou TEXT encoding=PLAIN compression=SNAPPY, fix_gps TEXT encoding=PLAIN compression=SNAPPY, fix_glonass TEXT encoding=PLAIN compression=SNAPPY, fix_galileo TEXT encoding=PLAIN compression=SNAPPY, region_id INT32 encoding=PLAIN compression=SNAPPY, veh_code TEXT encoding=PLAIN compression=SNAPPY, veh_plate_num TEXT encoding=PLAIN compression=SNAPPY, own_unit_id INT32 encoding=PLAIN compression=SNAPPY, use_unit_id INT32 encoding=PLAIN compression=SNAPPY);";
            String endTemplateSql = templateSql.replaceAll("CONFIG_TEMPLATE_NAME", IOTDB_TEMPLATE_NAME);
            sessionPool.executeNonQueryStatement(endTemplateSql);
            logger.info("创建设备模板成功,名称为====>>{}", IOTDB_TEMPLATE_NAME);
            //将设备模板绑定到数据库路劲
            String dataBaseSql = "set DEVICE TEMPLATE CONFIG_TEMPLATE_NAME to IOTDB_DATABASE_NAME;";
            String newDataBaseSql = dataBaseSql.replaceAll("CONFIG_TEMPLATE_NAME", IOTDB_TEMPLATE_NAME);
            String endDataBaseSql = newDataBaseSql.replaceAll("IOTDB_DATABASE_NAME", IOTDB_DATABASE_NAME);
            sessionPool.executeNonQueryStatement(endDataBaseSql);
            logger.info("激活设备模板成功,数据库路径为====>>{}", IOTDB_DATABASE_NAME);
            return "SUCCESS";
        } catch (IoTDBConnectionException e) {
            logger.error("IoTDBConnectionException", e);
            return "Fail";
        } catch (StatementExecutionException e) {
            logger.error("StatementExecutionException", e);
            return "Fail";
        }
    }

    /*
     * 批量插入数据到IotDB
     */
    public static void batchInsertDataToDB(List<TerminalData> dataList) {
        SessionPool sessionPool = IotDBConfig.getConnection();
        List<String> deviceIds = new ArrayList<>();
        List<List<String>> measurementsList = new ArrayList<>();
        List<List<TSDataType>> typeList = new ArrayList<>();
        List<Long> times = new ArrayList<>();
        List<List<Object>> valueList = new ArrayList<>();
        try {
            if (dataList != null && dataList.size() > 0) {
                //填充数据List
                for (TerminalData terminalData : dataList) {
                    String deviceId = IOTDB_DATABASE_PATH + PRE_IOTDB_SN + terminalData.getTerminalSn();
                    //String deviceId = IOTDB_DATABASE_PATH + PRE_IOTDB_SN + terminalData.getTerminalSIM();
                    List<String> measurements = new ArrayList<>();
                    //填充字段名
                    measurements.add("terminal_sn");
                    measurements.add("terminal_sim");
                    measurements.add("isFixed");
                    measurements.add("longitude");
                    measurements.add("latitude");
                    measurements.add("elevation");
                    measurements.add("direction");
                    measurements.add("terminal_time");
                    measurements.add("terminal_miles");
                    measurements.add("terminal_miles_aday");
                    measurements.add("speed");
                    measurements.add("terminal_off_line");
                    measurements.add("terminal_over_speed");
                    measurements.add("terminal_tired_drive");
                    measurements.add("terminal_gnss_error");
                    measurements.add("terminal_ant_open");
                    measurements.add("terminal_ant_short");
                    measurements.add("terminal_under_vol");
                    measurements.add("terminal_no_vol");
                    measurements.add("terminal_acc_status");
                    measurements.add("terminal_net_rssi");
                    measurements.add("terminal_gnss_cnt");
                    measurements.add("fix_beidou");
                    measurements.add("fix_gps");
                    measurements.add("fix_glonass");
                    measurements.add("fix_galileo");
                    measurements.add("region_id");
                    measurements.add("veh_code");
                    measurements.add("veh_plate_num");
                    measurements.add("own_unit_id");
                    measurements.add("use_unit_id");


                    List<TSDataType> types = new ArrayList<>();
                    //填充字段类型
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.FLOAT);
                    types.add(TSDataType.FLOAT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);
                    types.add(TSDataType.TEXT);

                    //填充字段具体值
                    List<Object> values = new ArrayList<>();
                    values.add(terminalData.getTerminalSn());
                    values.add(terminalData.getTerminalSIM());
                    values.add(terminalData.getIsFixed());
                    if (StringUtils.isBlank(terminalData.getLongitude())) {
                        terminalData.setLongitude("0");
                    }
                    if (StringUtils.isBlank(terminalData.getLatitude())) {
                        terminalData.setLatitude("0");
                    }
                    values.add(Float.parseFloat(terminalData.getLongitude()));
                    values.add(Float.parseFloat(terminalData.getLatitude()));
                    values.add(terminalData.getElevation());
                    values.add(terminalData.getDirection());
                    values.add(terminalData.getTerminalTime());
                    values.add(terminalData.getTerminalMiles());
                    values.add(terminalData.getTerminalMilesAday());
                    values.add(terminalData.getSpeed());
                    values.add(terminalData.getTerminalOffLine());
                    values.add(terminalData.getTerminalOverSpeed());
                    values.add(terminalData.getTerminalTiredDrive());
                    values.add(terminalData.getTerminalGnssError());
                    values.add(terminalData.getTerminalAntOpen());
                    values.add(terminalData.getTerminalAntShort());
                    values.add(terminalData.getTerminalUnderVol());
                    values.add(terminalData.getTerminalNoVol());
                    values.add(terminalData.getTerminalAccStatus());
                    values.add(terminalData.getTerminalNetRssi());
                    values.add(terminalData.getTerminalGnssCnt());
                    values.add(terminalData.getFixBeidou());
                    values.add(terminalData.getFixGps());
                    values.add(terminalData.getFixGlonass());
                    values.add(terminalData.getFixGalileo());
                    values.add(terminalData.getRegionCode());
                    values.add(terminalData.getVehiclesCode());
                    values.add(terminalData.getVehPlateNum());
                    values.add(terminalData.getOwnUnitId());
                    values.add(terminalData.getUseUnitId());

                    //设备编码路径
                    deviceIds.add(deviceId);

                    String terminalTime = terminalData.getTerminalTime();
                    //存在时区的时间转为毫秒
//                long time= DateUtils.strToLocalDate(terminalTime);
                    //普通时间转为毫秒
                    long time = DateUtils.strToTimes(terminalTime);
                    //时序
                    times.add(time);
                    measurementsList.add(measurements);
                    typeList.add(types);
                    valueList.add(values);
//                logger.info("数据添加到IotDB入库List,设备编号====>>{}", deviceId);
                }
                //批量入库
                logger.info("本次批量入库的数据List大小为====>>{}", dataList.size());
                sessionPool.insertAlignedRecords(deviceIds, times, measurementsList, typeList, valueList);
            }
        } catch (IoTDBConnectionException e) {
            logger.error("IoTDBConnectionException", e);
        } catch (StatementExecutionException e) {
            logger.error("StatementExecutionException", e);
        } catch (RuntimeException e) {
            logger.error("RuntimeException", e);
        }
    }
}